/******************************************************************************
 * FILE: mpi_bug6.c
 * DESCRIPTION:
 *   This program attempts to compare isend/irecv with send/irecv, but there is
 *   a bug. Under AIX it will cause program failure. Intel Linux ignores it.
 *   4 tasks are required.
 * AUTHOER: Blaise Barney
 * LAST REVISED: 04/13/05
 ******************************************************************************/
#include <mpi/mpi.h>
#include <stdio.h>
#include <stdlib.h>

#define COMM MPI_COMM_WORLD
#define REPS 1000
#define DISP 100

int main(int argc, char *argv[]) {
    int numtasks, rank, buf, i, rc, dest, src, offset, nreqs;
    double T1, T2;

    MPI_Init(&argc, &argv);
    MPI_Comm_size(COMM, &numtasks);
    MPI_Comm_rank(COMM, &rank);

    /* Require 4 tasks */
    if (rank == 0) {
        if (numtasks != 4) {
            printf("ERROR: Number of tasks must be 4. Quitting.\n");
            MPI_Abort(COMM, rc);
        }
        printf("Starting isend/irecv send/irecv test...\n");
    }

    /* Use barriers for clean output */
    MPI_Barrier(COMM);
    printf("Task %d starting...\n", rank);
    MPI_Barrier(COMM);

    T1 = MPI_Wtime(); /* start the clock */

    /* Tasks 0 and 1 do the isend/irecv test.
     *  Determine who to send/receive with. nreqs specifies how many
     * non-blocking operation request handles to capture. offset is where the
     * task should store each request as it is captured in the reqs() array. */
    if (rank < 2) {
        nreqs = REPS * 2;
        MPI_Request reqs[REPS * 2]{MPI_REQUEST_NULL};
        MPI_Status stats[REPS * 2];
        if (rank == 0) {
            src = 1;
            dest = src;
            offset = 0;
        } else {
            src = 0;
            dest = src;
            offset = 0;
        } /* Do the non-blocking send and receive operations */
        for (i = 0; i < REPS; i++) {
            MPI_Isend(&rank, 1, MPI_INT, dest, 1, COMM, &reqs[offset]);
            MPI_Irecv(&buf, 1, MPI_INT, src, 1, COMM, &reqs[offset + 1]);
            offset += 2;
        }
        MPI_Waitall(nreqs, reqs, stats);
    }

    /* Tasks 2 and 3 do the send/irecv test.
       Determine who to send/receive with. nreqs specifies how many non-blocking
       operation request handles to capture. offset is where the task should
       store each request as it is captured in the reqs() array.  */
    if (rank > 1) {
        nreqs = REPS;
        MPI_Request reqs[REPS];
        MPI_Status stats[REPS];
        /* Task 2 does the blocking send operation */
        if (rank == 2) {
            dest = 3;
            for (i = 0; i < REPS; i++) {
                MPI_Send(&rank, 1, MPI_INT, dest, 2, COMM);
            }
            offset = REPS;
        }

        /* Task 3 does the non-blocking receive operation */
        if (rank == 3) {
            src = 2;
            offset = 0;
            for (i = 0; i < REPS; i++) {
                MPI_Irecv(&buf, 1, MPI_INT, src, 2, COMM, &reqs[offset]);
                offset += 1;
            }
        }
        printf("checkpoint %d\n", rank);
        if (rank == 3) {
            MPI_Waitall(nreqs, reqs, stats);
        }
        printf("checkpoint %d\n", rank);
    }

    /* Wait for all non-blocking operations to complete and record time */
    T2 = MPI_Wtime(); /* end time */
    MPI_Barrier(COMM);

    printf("Task %d time(wall)= %lf sec\n", rank, T2 - T1);

    MPI_Finalize();
}
